use crate::tools::{common, db};
use chrono::{prelude::*, DateTime, Days, Duration, Local, Months};
use log::{error, info, warn};
use rusqlite::{params, Connection, Error, Result};
use serde_json::{json, Value};
use uuid::Uuid;

#[derive(serde::Serialize)]
pub struct Todo {
    pub t_id: String,
    pub t_title: String,
    pub end_date: String,
    pub remind_time: String,
    pub repeat_rule: String,
    pub rule_detail: Option<String>,
    pub priority_level: String,
    pub t_state: String,
    pub first_end_date: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
struct RuleDetail {
    interval: u32,
    unit: String,
    expand: Vec<String>,
}

pub fn save(
    t_id: &str,
    t_title: &str,
    end_date: &str,
    remind_time: &str,
    repeat_rule: &str,
    rule_detail: &str,
    priority_level: &str,
    first_end_date: &str,
) -> Result<()> {
    //let bind = common::DB_CONNECT.lock().unwrap();
    //let conn = bind.as_ref().unwrap();

    let conn;
    match db::connect() {
        Ok(con) => {
            conn = con;
        }
        Err(e) => {
            return Err(e);
        }
    }

    if t_id.is_empty() {
        //新增
        let uuid = Uuid::new_v4();
        let local_time: DateTime<Local> = Local::now();
        let format_time = local_time.format("%Y-%m-%d %H:%M:%S").to_string();

        let sql = r#"insert into tools_todo 
                    (t_id,t_title,t_state,end_date,remind_time,repeat_rule,create_date,update_date,rule_detail,priority_level,first_end_date) 
                    values 
                    (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11)"#;
        match conn.execute(
            sql,
            params![
                uuid.to_string(),
                t_title,
                "0",
                end_date,
                remind_time,
                repeat_rule,
                &format_time,
                &format_time,
                rule_detail,
                priority_level,
                first_end_date
            ],
        ) {
            Ok(_) => Ok(()),
            Err(e) => {
                error!("{:?}", e);
                return Err(e);
            }
        }
    } else {
        //修改
        let local_time: DateTime<Local> = Local::now();
        let format_time = local_time.format("%Y-%m-%d %H:%M:%S").to_string();

        let sql = r"update tools_todo set 
                        t_title=?1,end_date=?2,remind_time=?3,repeat_rule=?4,rule_detail=?5,priority_level=?6,update_date=?7,first_end_date=?8 
                    where t_id=?9";
        match conn.execute(
            sql,
            params![
                t_title,
                end_date,
                remind_time,
                repeat_rule,
                rule_detail,
                priority_level,
                format_time,
                end_date,
                t_id
            ],
        ) {
            Ok(_) => Ok(()),
            Err(e) => {
                error!("{:?}", e);
                return Err(e);
            }
        }
    }
}
/*
完成待办时需要处理有循环属性的任务，自动创建下一个
*/
pub fn done_todo(t_id: &str) -> Result<()> {
    let bind = common::DB_CONNECT.lock().unwrap();
    let conn = bind.as_ref().unwrap();

    //检测任务，如果是重复任务，创建下一周期任务
    let sql = "select * from tools_todo where t_id=?1";
    let stmt = conn.prepare(sql);
    let mut binding = stmt?;
    let todo = binding.query_row([t_id], |row| {
        Ok(Todo {
            t_id: row.get("t_id")?,
            t_title: row.get("t_title")?,
            end_date: {
                match row.get("end_date") {
                    Ok(v) => v,
                    Err(_) => String::new(),
                }
            },
            remind_time: {
                match row.get("remind_time") {
                    Ok(v) => v,
                    Err(_) => String::new(),
                }
            },
            repeat_rule: row.get("repeat_rule")?,
            rule_detail: row.get::<_, Option<String>>("rule_detail")?,
            priority_level: row.get("priority_level")?,
            t_state: row.get("t_state")?,
            first_end_date: {
                match row.get("first_end_date") {
                    Ok(v) => v,
                    Err(_) => String::new(),
                }
            },
        })
    });
    match todo {
        Ok(td) => {
            println!("{}", td.t_id);
            create_repeat_todo(&td);
            info!("create_repeat_todo end.")
        }
        Err(e) => {
            error!("{:?}", e);
        }
    }

    let local_time: DateTime<Local> = Local::now();
    let format_time = local_time.format("%Y-%m-%d %H:%M:%S").to_string();

    let sql = "update tools_todo set t_state='1',update_date=?1 where t_id=?2";
    match conn.execute(sql, params![format_time, t_id]) {
        Ok(_) => Ok(()),
        Err(e) => {
            error!("更新失败！{:?}", e);
            Err(e)
        }
    }
}
fn create_repeat_todo(todo: &Todo) -> bool {
    info!("into create_repeat_todo");
    if todo.repeat_rule == "noRepeat" {
        return true;
    }
    //这里有问题，上一个方法锁定了数据库连接，会导致这里拿不到数据库连接，

    //let bind = common::DB_CONNECT.lock().unwrap();
    //let conn = bind.as_ref().unwrap();
    let end_time_format = "%Y-%m-%d";

    info!("into create_repeat_todo 1");

    let mut end_time = String::new();
    let mut end_offset: i64 = 0;
    if todo.repeat_rule == "everyDay" {
        info!("into create_repeat_todo 2");
        end_offset = 1;
    } else if todo.repeat_rule == "workDay" {
        info!("into create_repeat_todo 3");
        //获取下一个工作日
        let np = NaiveDate::parse_from_str(&todo.end_date, &end_time_format).unwrap();
        let weekday = np.weekday();
        //let weekday = Local::now().date_naive().weekday();
        let mut offset = 0;
        match weekday {
            Weekday::Fri => offset = 3,
            Weekday::Sat => offset = 2,
            _ => offset = 1,
        }
        info!("into create_repeat_todo 4");

        end_offset = offset;
    } else if todo.repeat_rule == "everyWeek" {
        //获取下周的今天

        info!("into create_repeat_todo 5");
        end_offset = 7;
    } else if todo.repeat_rule == "everyMonth" {
        info!("into create_repeat_todo 6");
        //月份+1，拼接first_end_date的天
        let date = NaiveDate::parse_from_str(&todo.end_date, end_time_format).unwrap();
        let next_date = get_next_day(&todo, &end_time_format, 1);

        let ndate = next_date - date;
        end_offset = ndate.num_days();
    } else if todo.repeat_rule == "everyYear" {
        info!("into create_repeat_todo 7");
        let date = NaiveDate::parse_from_str(&todo.end_date, &end_time_format).unwrap();
        let next_date = get_next_day(&todo, &end_time_format, 12);

        let ndate = next_date - date;
        end_offset = ndate.num_days();
    } else if todo.repeat_rule == "custom" {
        info!("into custom.");
        //自定义规则计算
        match &todo.rule_detail {
            Some(detail) => {
                let r_detail = detail.replace("\\", "");
                let r_detail = r_detail.trim_start_matches('"');
                let r_detail = r_detail.trim_end_matches('"');
                info!("r_detail: {}", &r_detail);
                let json_obj: RuleDetail = serde_json::from_str(&r_detail).unwrap();
                let interval = json_obj.interval;
                let unit = json_obj.unit;
                let expand = json_obj.expand;

                let offset: i64 = interval as i64;

                if "day" == unit {
                    end_offset = offset as i64;
                    info!("into day unit,end_offset is {}", &end_offset);
                } else if "week" == unit {
                    /*let next_day = Local::now().naive_local();
                    let next_day = next_day.checked_add_days(Days::new((7*offset) as u64));
                    match next_day{
                        Some(day)=>end_time = day.format(&end_time_format).to_string(),
                        None=>{}
                    }*/
                    //如果重复规则是周，并且没有指定是周几，那么end_date可以作为每周周几的计算条件
                    //如果指定了周几，那么end_date无效，
                    //当前是周几，找到当前日期最近的一个
                    //let weekday = Local::now().date_naive().weekday();
                    let weekday = NaiveDate::parse_from_str(&todo.end_date, &end_time_format)
                        .unwrap()
                        .weekday();
                    let mut num = 0;
                    match weekday {
                        Weekday::Mon => num = 1,
                        Weekday::Tue => num = 2,
                        Weekday::Wed => num = 3,
                        Weekday::Thu => num = 4,
                        Weekday::Fri => num = 5,
                        Weekday::Sat => num = 6,
                        Weekday::Sun => num = 7,
                    }

                    let mut b = expand.clone();
                    b.sort();

                    let mut temp = 0;
                    for item in &b {
                        let x: i32 = item.parse().unwrap();
                        if x > num {
                            temp = x;
                            break;
                        }
                    }

                    end_offset = 7 * offset;
                } else if "month" == unit {
                    //let today = Local::now();
                    //let date = today.date_naive();
                    let date = NaiveDate::parse_from_str(&todo.end_date, &end_time_format).unwrap();
                    let next_date = date.checked_add_months(Months::new(offset as u32));
                    match next_date {
                        Some(nd) => {
                            let ndate = nd - date;
                            end_offset = ndate.num_days();
                        }
                        None => {}
                    }
                } else if "year" == unit {
                    //let next_date = Local::now().date_naive().checked_add_months(Months::new((12*offset) as u32));
                    let md = NaiveDate::parse_from_str(&todo.end_date, &end_time_format).unwrap();
                    let next_date = md.checked_add_months(Months::new((12 * offset) as u32));
                    match next_date {
                        Some(nd) => {
                            let ndate = nd - md;
                            end_offset = ndate.num_days();
                        }
                        None => {}
                    }
                }
            }
            None => {}
        }
    }

    let nf = NaiveDate::parse_from_str(&todo.end_date, &end_time_format).unwrap();
    let nf = nf + Duration::days(end_offset);

    end_time = nf.format(&end_time_format).to_string();

    let mut detail = String::new();
    match &todo.rule_detail {
        Some(dt) => detail = dt.clone(),
        None => {}
    }
    match save(
        "",
        &todo.t_title,
        &end_time,
        &todo.remind_time,
        &todo.repeat_rule,
        &detail,
        &todo.priority_level,
        &todo.first_end_date,
    ) {
        Ok(()) => true,
        Err(e) => {
            error!("{:?}", e);
            false
        }
    }
}
fn get_next_day(todo: &Todo, end_time_format: &str, month_count: u32) -> NaiveDate {
    let date = NaiveDate::parse_from_str(&todo.end_date, end_time_format).unwrap();
    let next_date = date.checked_add_months(Months::new(month_count)).unwrap();

    let x = next_date.to_string();
    let t_end = x.as_str();
    let t_end = &t_end[0..7];

    let sux = &todo.first_end_date[8..10];
    let t_end = format!("{}-{}", t_end, sux);
    let t_end = t_end.as_str();

    let year_month = NaiveDate::parse_from_str(&t_end, end_time_format);

    let ret_date;
    match year_month {
        Ok(v) => ret_date = v,
        Err(_) => {
            ret_date =
                NaiveDate::parse_from_str(get_last_day(&t_end).as_str(), end_time_format).unwrap()
        }
    }

    ret_date
}
/// 获取本月最后一天
fn get_last_day(date: &str) -> String {
    let md = NaiveDate::parse_from_str(&date, "%Y-%m-%d");
    match md {
        Ok(sd) => sd.to_string(),
        Err(_) => {
            let day = &date[8..10];
            let nday: u8 = day.parse().unwrap();
            let nday = nday - 1;
            let day = format!("{}-{}", &date[0..7], nday);
            get_last_day(day.as_str())
        }
    }
}

pub fn get_todo_list(t_state: &str) -> Result<Vec<Todo>> {
    let bind = common::DB_CONNECT.lock().unwrap();
    let conn = bind.as_ref().unwrap();

    let stmt = conn.prepare(
        r"SELECT *
        FROM tools_todo where t_state=?1
        ORDER BY priority_level DESC, 
                 CASE WHEN end_date = '' THEN 1 ELSE 0 END, 
                 end_date ASC",
    );
    let mut binding = stmt?;
    let todo_iter = match binding.query_map([t_state], |row| {
        Ok(Todo {
            t_id: row.get("t_id")?,
            t_title: row.get("t_title")?,
            end_date: {
                match row.get("end_date") {
                    Ok(v) => v,
                    Err(_) => String::new(),
                }
            },
            remind_time: {
                match row.get("remind_time") {
                    Ok(v) => v,
                    Err(_) => String::new(),
                }
            },
            repeat_rule: row.get("repeat_rule")?,
            rule_detail: row.get::<_, Option<String>>("rule_detail")?,
            priority_level: row.get("priority_level")?,
            t_state: row.get("t_state")?,
            first_end_date: {
                match row.get("first_end_date") {
                    Ok(v) => v,
                    Err(_) => String::from(""),
                }
            },
        })
    }) {
        Ok(iter) => iter,
        Err(_) => {
            return Ok(Vec::new());
        }
    };

    let mut all_todo = Vec::new();
    for c in todo_iter {
        all_todo.push(c?);
    }
    Ok(all_todo)
}

pub fn del_todo(t_id: &str) -> Result<()> {
    let bind = common::DB_CONNECT.lock().unwrap();
    let conn = bind.as_ref().unwrap();

    let sql = "delete from tools_todo where t_id=?1";
    conn.execute(sql, params![t_id])?;
    info!("已成功删除:{}", t_id);
    Ok(())
}
